import pygame
from pygame.locals import *
import pygame.surfarray

import os
from general_utilities import *

from ConfigParser import *
conf = SafeConfigParser()
if os.path.exists("config/config.txt"):
    file = open("config/config.txt","r")
    conf.readfp(file)
    file.close()
if not conf.has_section("sound"):
    conf.add_section("sound")
if not conf.has_option("sound","sound"):
    conf.set("sound","sound","on")
if not conf.has_section("display"):
    conf.add_section("display")
if not conf.has_option("display","fullscreen"):
    conf.set("display","fullscreen","on")

# initialise sound
from sound import *
sound_mode = conf.get("sound","sound")
sound.initialise(sound_mode=="on","data/sounds")

from graphics_utilities import *
from screen_info import display, GuilessDialog, GuilessSplashScreen, GuilessInputDialog, GuilessFileChooser

# initialise graphics
fullscreen_mode = conf.get("display","fullscreen")
display.initialise(fullscreen_mode=="on")

from file_info import *

from help_message import help_text

from ready_dungeon import *
from map import *
from entities import *
from actions import *
from powers import *
from los import *
from pathfinding import *
from glyphs import *
import gamestate
from gamestate import d

def quit_game(d):
    if display.get_yes_no('Save and quit?','Do you want to save the game and quit?'):
        files.end_game(d,display,0) 

def new_game(junk):
    # Return (0,junk) to quit, (1,junk) to start a new game, or (2,junk) to load an old one
    # get a game name
    ok = 0
    while not ok:
        gamename = GuilessInputDialog("New game","Enter a savefile name for this game (e.g. MyGame)",is_fullscreen=display.is_fullscreen,allow_quit=1)
        if gamename==None or gamename=="":
            pass
        elif files.is_save_file_free("save",gamename):
            ok = 1
        else:
            display.simple_info_dialog("New game","That name is already taken, please choose another",allow_quit=1)
    files.set_savefilename("save/"+gamename+".sav",gamename)
    # blank the screen
    display.clear_map()
    display.no_map_updates_yet = 1
    if not display.object_monster_pics_filled_in:
        # fill in object and monster pictures - odd place to put it? Avoiding circular dependencies...
        display.fill_in_object_pics(olib)
        display.fill_in_monster_pics(mlib)
        display.object_monster_pics_filled_in = 1
    # create the game
    gamestate.d = gamestate.Dungeon(D_ENTRY,-1,1,1,[])
    # display the screen
    display.no_map_updates_yet = 0
    display.refill_character_pane(gamestate.d)
    display.draw_map(gamestate.d)
    display.blit_all_to_screen(gamestate.d)  
    # write an introduction message
    display.send_message(gamestate.d.l.entry_message)  
    # play the game
    return play_game(gamestate.d)    

def tutorial(junk):
    # Return (0,junk) to quit, (1,junk) to start a new game, or (2,junk) to load an old one
    gamename = 'tutorial'
    savefilename = "save/"+gamename+".sav"
    files.set_savefilename(savefilename,gamename)
    # blank the screen
    display.clear_map()
    display.no_map_updates_yet = 1
    # fill in object and monster pictures - odd place to put it? Avoiding circular dependencies...
    display.fill_in_object_pics(olib)
    display.fill_in_monster_pics(mlib)
    # create the game
    gamestate.d = gamestate.Dungeon(D_TUTORIAL,-1,1,1,[])
    # display the screen
    display.no_map_updates_yet = 0
    display.refill_character_pane(gamestate.d)
    display.draw_map(gamestate.d)
    display.blit_all_to_screen(gamestate.d)  
    # write an introduction message
    display.send_message(gamestate.d.l.entry_message)  
    # play the game
    return play_game(gamestate.d)    

def get_savefile_to_open(can_cancel):
    # return the savefile, or None
    savefile_with_path = None
    savefile_ok = 0
    while 1:
        savefile_with_path = GuilessFileChooser("Which game do you want to resume?","save",can_cancel,is_fullscreen=display.is_fullscreen,allow_quit=1)
        if savefile_with_path is None:
            return None
        savefile_ok = (savefile_with_path is not None and savefile_with_path is not "" and savefile_with_path[len(savefile_with_path)-4:]==".sav")
        if savefile_ok:
            return savefile_with_path
        else:
            display.simple_info_dialog("Load game","That is not a valid savefile...",allow_quit=1)

def load_game(savefile_with_path):
    # Return (0,junk) to quit, (1,junk) to start a new game, or (2,junk) to load an old one
    if gamestate.d is not None:
        # save the old game
        display.send_message("Saving your current game...")
        files.save_gamestate(gamestate.d)
    # blank the screen
    display.clear_map()
    display.no_map_updates_yet = 1
    if not display.object_monster_pics_filled_in:
        # fill in object and monster pictures - odd place to put it? Avoiding circular dependencies...
        display.fill_in_object_pics(olib)
        display.fill_in_monster_pics(mlib)
        display.object_monster_pics_filled_in = 1
    # load the game
    gamestate.d = files.load_gamestate(savefile_with_path)
    # display the screen
    display.no_map_updates_yet = 0
    display.refill_character_pane(gamestate.d)
    display.draw_map(gamestate.d)
    display.show_glyphs(gamestate.d)
    display.blit_all_to_screen(gamestate.d)  
    # write an introduction message
    display.send_message("Game resumed...")  
    # play the game
    return play_game(gamestate.d) 

def do_minimap(d,display):
    d.clear_action_stack()
    display.draw_minimap(d,P_DETECT_ENEMIES in d.get_character().info().powers)
    no_input = 1
    while (no_input):
        events = pygame.event.get()
        for event in events:
            if event.type==KEYDOWN or event.type==MOUSEBUTTONUP:
                no_input = 0
    display.draw_map(d)
    display.blit_map_to_screen() 
                                        
def do_powers(d,display):
    d.clear_action_stack()
    powers_to_dump = []
    any_shown = 0
    for power in d.get_character().info().powers:
        if power in power_names.keys():
            if power in d.get_character().learned_powers:
                any_shown = 1
                dump_it = display.show_power("",power,power_names[power],power_descriptions[power],ask_to_dump=(d.get_character().level >= 6))
                if dump_it:
                    powers_to_dump.append(power)
            else:
                any_shown = 1
                display.show_power("",power,power_names[power],power_descriptions[power])
    if len(powers_to_dump)>0:
        for power in powers_to_dump:
            d.get_character().learned_powers.remove(power)
        d.get_character().recalc_hp_mp()
        d.get_character().info().recalc(d)
    if not any_shown:
        display.send_message("You don't have any special powers yet.")

def play_game(d):
    # main game loop - probably pull this out of here in the fullness of time, or at least shift its components out
    # Careful about local variables here; they only get saved and reloaded if you insert them into gamestate
    # Return (1,junk) to start a new game, or (2,filename) to open an old game.
    d.clear_glyph_stack()
    mouse_button_down = 0    
    missile_hilite = 0
    while(1):
        d.new_turn()
        if d.get_character().has_escaped():
            wait(ms=500)
            display.send_message(d.l.exit_message)
            wait(ms=2000)
            exit_to = d.l.tile_at(d.get_character().pos).exit_to                    
            # blank the screen
            display.clear_map()
            display.no_map_updates_yet = 1
            if d.tutorial:
                # play a proper game
                display.simple_info_dialog("New game","Now a real game will begin...")
                return (1,0) # new game
            elif d.escaped_to[0]==D_OUTSIDE:
                files.exit_dungeon(d,display,"You have escaped the dungeon! Though you've not found the Orb, at least you survived. You return to your family and live out the rest of a happy and uneventful life...")
            else:
                # recreate the dungeon
                GuilessSplashScreen(None,[d.l.exit_message,"","","Loading..."],stop_immediately=1) 
                escaped_to_dungeon = d.escaped_to[0]
                escaped_to_level = d.escaped_to[1]
                while 1: # TO DO - this is the infinite loop
                    d = gamestate.new_dungeon(d,escaped_to_dungeon,escaped_to_level)
                    if not escaped_to_dungeon==D_MAIN_DUNGEON:
                        break
                # display the screen
                display.no_map_updates_yet = 0
                display.refill_character_pane(d)
                display.draw_map(d)
                display.blit_all_to_screen(d) 
                display.send_message(d.l.entry_message)
                if escaped_to_dungeon<>D_MAIN_DUNGEON and escaped_to_dungeon<>D_OUTSIDE:
                    display.simple_info_dialog("Entering a new area",d.l.entry_message)
            continue
        for group in d.gr:
            if not group.extinct:
                d.current_group = group  
                d.current_group.moved_this_turn = []
                d.finished_group_moves = 0
                d.current_group.start_of_turn_update(d)
                while not d.finished_group_moves:
                    d.current_actor = d.current_group.get_next_actor(d)
                    if d.current_actor is not None:                      
                        d.can_move = d.current_actor.begin_new_turn(d)
                        if d.current_actor.is_character:
                            # give some zap
                            d.get_character().zap = min(d.get_character().original_zap,d.get_character().zap+1)
                            # empty the 'no need to play' noise-type list
                            d.noise_types_to_suppress = []
                            # do an ambient message?
                            if not display.message_received:
                                for possible_message in (d.l.ambient_messages + d.l.level_at(d.get_active_pos()).ambient_messages + d.l.room_at(d.get_active_pos()).ambient_messages):
                                  if possible_message.message<>"" and possible_message.message==display.last_ambient_message:
                                      continue
                                  if random.random()<possible_message.prob:
                                    display.last_ambient_message = possible_message.message
                                    n = Notification(d.get_active_pos())
                                    if possible_message.message is not None:
                                      n.set_message(possible_message.message,0)
                                    if possible_message.sound is not None:
                                      n.set_sound(possible_message.sound,possible_message.volume)
                                    n.play(d)  
                                    break  
                            display.message_received = max(0,display.message_received-1)
                        confusion_check = random.random()
                        while d.can_move and d.current_actor.energy > 0:
                            a = []                                 
                            if d.current_actor.is_character:
                                # redraw map if necessary
                                display.move_view_to(d,d.current_actor.pos)
                                # update energy bar if necessary
                                now = pygame.time.get_ticks()
                                elapsed = float(now - display.last_zap_update_at)  # ms
                                display.last_zap_update_at = now
                                d.get_character().zap = max(0,d.get_character().zap - elapsed/1000)
                                display.redraw_bars(d.get_character())
                            confused = 0
                            if d.current_actor.has_condition(C_CONFUSED) and confusion_check<0.7:
                                random_dir = DIRS_AROUND[random.randint(0,7)]
                                random_space = get_pos_in_dir(d,d.current_actor.pos,random_dir)
                                if d.l.valid(random_space):
                                    if d.current_actor.is_character:
                                        n = Notification(d.current_actor.pos)
                                        n.set_message("You reel about...",0)
                                        n.set_sound("confuse",volume_mult=0.4)
                                        n.play(d)
                                        wait(ms=200)
                                    next_action = make_basic_move_action(d,d.current_actor,random_space)                                                   
                                    a.append(next_action)
                                    confused = 1
                            if d.current_actor.is_character and not confused and not d.current_actor.out_of_control():
                                # process mouse input
                                events = pygame.event.get()
                                for event in events:
                                    try_making_an_action = 1
                                    if event.type == QUIT:
                                        quit_game(d)
                                        break
                                    if missile_hilite and (event.type == MOUSEBUTTONDOWN or event.type==KEYDOWN):
                                        missile_hilite = 0
                                        display.refill_character_pane(d) 
                                        pygame.display.flip()
                                    if event.type == MOUSEBUTTONDOWN and event.button>1:
                                        # not interested
                                        continue
                                    elif event.type == KEYDOWN and event.unicode in ['m','M']: 
                                        do_minimap(d,display)
                                        break
                                    elif event.type == KEYDOWN and event.unicode in ['p','P']: 
                                        do_powers(d,display)
                                        break
                                    elif event.type == MOUSEBUTTONUP and event.button>1:
                                        # right-click for info
                                        if display.new_rect.collidepoint(event.pos):
                                            display.simple_info_dialog('"New Game" button','Press this button to quit your current game and start a new one.')
                                        elif display.load_rect.collidepoint(event.pos):
                                            display.simple_info_dialog('"Load Game" button','Press this button to quit your current game and reload an older one.')
                                        elif display.quit_rect.collidepoint(event.pos):  
                                            display.simple_info_dialog('"Quit Game" button','Press this button to save the game and quit.')
                                        elif display.minimap_rect.collidepoint(event.pos): 
                                            display.simple_info_dialog('"Map" button','Press this button to view a map of the level you are on.')
                                        elif display.powers_rect.collidepoint(event.pos): 
                                            display.simple_info_dialog('"Powers" button','Press this button to see a list of the special powers you currently possess.')
                                        elif display.hp_rect.collidepoint(event.pos): 
                                            display.simple_info_dialog('Health bar','This is your health bar. You can regain health by eating food or drinking healing potions. When it drops to zero... your hero\'s adventure will be over!')
                                        elif display.mp_rect.collidepoint(event.pos): 
                                            display.simple_info_dialog('Magic bar','This is your magic bar. You will regain magic as you move about the dungeon.')
                                        elif display.energy_rect.collidepoint(event.pos): 
                                            display.simple_info_dialog('Energy bar','This is your energy bar. The quicker you move, the more energy you will have. If you have a lot of energy, sometimes you will get a bonus, like an extra quick move or an extra hard strike.')
                                        elif display.score_rect.collidepoint(event.pos): 
                                            display.simple_info_dialog('Score','This is your score. You can gain points by collecting coins and gems. When you get enough points, you will gain a level, and if you are wearing some garb, you may learn a new power.')
                                        elif display.melee_weapon_rect.collidepoint(event.pos):  
                                            display.simple_info_dialog('Melee weapon','Your present melee weapon (if any) is shown here. Click on it to see a description, or drag it onto the map to drop it.')
                                        elif display.missile_weapon_rect.collidepoint(event.pos):  
                                            display.simple_info_dialog('Missile weapon','Your present missile weapon (if any) is shown here. Click on it to see a description, or drag it onto the map to drop it.')
                                        elif display.item_rects[0].collidepoint(event.pos) or display.item_rects[1].collidepoint(event.pos) or display.item_rects[2].collidepoint(event.pos) or display.item_rects[3].collidepoint(event.pos) or display.item_rects[4].collidepoint(event.pos) or display.item_rects[5].collidepoint(event.pos):  
                                            display.simple_info_dialog('Item','One of your inventory items is shown here. Click on it to use it if it is a potion, or to see a description otherwise. You can also drag it onto the map to drop it.')
                                        elif display.key_rects[0].collidepoint(event.pos) or display.key_rects[1].collidepoint(event.pos) or display.key_rects[2].collidepoint(event.pos) or display.key_rects[3].collidepoint(event.pos) or display.key_rects[4].collidepoint(event.pos):
                                            display.simple_info_dialog('Key','One of your keys is shown here. Click on it to see a description. You can also drag it onto the map to drop it.')
                                        elif display.garb_rect.collidepoint(event.pos):
                                            display.simple_info_dialog('Garb','Your garb is shown here. Your garb determines your path in life and the powers you can learn: it also determines your base statistics (armor, magic resistance, speed, stealth). Click on it to see a description and the current values of your stats.')
                                        elif display.message_rect.collidepoint(event.pos):
                                            display.simple_info_dialog('Message window','Informative messages are shown here. You can scroll down to display recent messages.')
                                        elif display.button_fullscreenise_rect is not None and display.button_fullscreenise_rect.collidepoint(event.pos):
                                            display.simple_info_dialog('Full-screen button','Click here to make the game fill the entire screen. You will need this if your resolution is 1024x768.')
                                        elif display.button_iconify_rect is not None and display.button_iconify_rect.collidepoint(event.pos):
                                            display.simple_info_dialog('Minimise button','Click here to make the game drop down to the taskbar.')
                                        elif display.button_windowise_rect is not None and display.button_windowise_rect.collidepoint(event.pos):
                                            display.simple_info_dialog('Windowise button','If your resolution is better than 1024x768, you can click here to play the game in a moveable window.')
                                        elif display.button_quit_rect is not None and display.button_quit_rect.collidepoint(event.pos):
                                            display.simple_info_dialog('Quit button','Press this button to save the game and quit.')
                                        elif display.button_sound_rect is not None and display.button_sound_rect.collidepoint(event.pos):
                                            display.simple_info_dialog('Sound button','Click here to turn the sound on or off.')
                                        else:
                                            pos_clicked = mouse_pos_to_map_pos(d,display,event.pos)
                                            if pos_clicked is not None:
                                                occupant = d.get_occupant(pos_clicked)
                                                if occupant is not None:
                                                    display.info_popup(entity=occupant)
                                                elif d.l.tile_at(pos_clicked).object is not None:
                                                    display.info_popup(object=d.l.tile_at(pos_clicked).object)
                                                else:
                                                    display.info_popup(tile=d.l.tile_at(pos_clicked))
                                    elif event.type == MOUSEBUTTONDOWN:
                                        if display.message_area.slider_box_rect.collidepoint(event.pos):
                                            pressed_time = pygame.time.get_ticks()
                                            last_slide_time = pygame.time.get_ticks()
                                            first_y = event.pos[1]
                                            last_y = event.pos[1]
                                            scroll_amount = 5
                                            last_move_up = 0
                                            while 1:
                                                events = pygame.event.get()
                                                shifted = 0
                                                broken = 0
                                                for event in events:
                                                    if event.type == QUIT:
                                                        quit_game(d)
                                                        break
                                                    elif event.type == MOUSEMOTION:
                                                        last_y = event.pos[1]
                                                        shifted = 1
                                                    elif event.type == MOUSEBUTTONUP:
                                                        broken = 1 
                                                        break
                                                if broken:
                                                    break
                                                if last_slide_time < pygame.time.get_ticks()-10 or shifted:
                                                    last_slide_time = pygame.time.get_ticks()
                                                    if last_y < first_y:
                                                        if last_move_up:
                                                            scroll_amount = min(30,scroll_amount + 1)
                                                        else:
                                                            scroll_amount = 5
                                                        display.message_area.scroll_up(scroll_amount)
                                                        last_move_up = 1
                                                        display.message_area.draw()
                                                        pygame.display.flip() 
                                                    else:
                                                        if not last_move_up:
                                                            scroll_amount = min(30,scroll_amount + 1)
                                                        else:
                                                            scroll_amount = 5
                                                        display.message_area.scroll_down(scroll_amount)
                                                        last_move_up = 0
                                                        display.message_area.draw()
                                                        pygame.display.flip() 
                                        elif display.slider_rect.collidepoint(event.pos):
                                            percent = 100 * float(event.pos[1] - display.slider_rect.top) / display.slider_rect.height
                                            display.message_area.scroll_to_percent(percent)
                                            display.message_area.draw()
                                            pygame.display.flip() 
                                        else:
                                            if display.message_area.offset < display.message_area.get_lowest_offset():
                                                display.message_area.scroll_to_bottom()
                                                display.message_area.draw()
                                                pygame.display.flip()                                                 
                                            mouse_button_down = 1
                                            mouse_button_down_time = pygame.time.get_ticks()
                                            mouse_button_pos_series = [event.pos]
                                    elif event.type == MOUSEMOTION:
                                        if not mouse_button_down and len(d.glyphs)==0 and d.get_character().missile_weapon is not None:
                                            map_pos = mouse_pos_to_map_pos(d,display,event.pos)
                                            on_enemy_with_los = 0
                                            if map_pos is not None:
                                                occupant = d.get_occupant(map_pos)
                                                if occupant is not None and not occupant.is_character and not occupant.type in (M_STATUE,M_CHAMELEON,M_INVISIBLE_STALKER) and not occupant.has_condition(C_INVISIBLE):
                                                    if d.get_character().is_enemy(occupant) and not adjacent(d,occupant.pos,d.get_character().pos):
                                                        if d.line_of_shot(d.get_character(),map_pos,[]):
                                                            on_enemy_with_los = 1
                                            if missile_hilite:
                                                if not on_enemy_with_los:
                                                    missile_hilite = 0
                                                    display.redraw_missile_weapon(d,hilite_missile=0)
                                                    # pygame.display.flip()
                                            else:
                                                if on_enemy_with_los:
                                                    missile_hilite = 1
                                                    display.redraw_missile_weapon(d,hilite_missile=1) 
                                                    # pygame.display.flip()
                                        if mouse_button_down and mouse_button_pos_series[len(mouse_button_pos_series)-1]<>event.pos:
                                            mouse_button_pos_series.append(event.pos)
                                    elif event.type == MOUSEBUTTONUP and mouse_button_down:
                                        mouse_button_down = 0
                                        mouse_button_up_time = pygame.time.get_ticks()
                                        if mouse_button_pos_series[len(mouse_button_pos_series)-1]<>event.pos:
                                            mouse_button_pos_series.append(event.pos)
                                        if display.new_rect.collidepoint(event.pos):
                                            if display.get_yes_no('Restart without saving?','Do you want to quit this game and start a new one? Your character will NOT be saved!'):
                                                if d.get_character().level<3 or display.get_yes_no('Are you sure?','Are you sure you want to restart? This character will be lost if you do...'):
                                                    return (1,0) # new game
                                        elif display.load_rect.collidepoint(event.pos):
                                            savefilename = get_savefile_to_open(1)
                                            if savefilename is not None:
                                                gamestate.d = d
                                                return (2,savefilename) # load game
                                            break
                                        elif display.quit_rect.collidepoint(event.pos):  
                                            quit_game(d)
                                            break
                                        elif display.minimap_rect.collidepoint(event.pos): 
                                            do_minimap(d,display)
                                            break
                                        elif display.powers_rect.collidepoint(event.pos):
                                            do_powers(d,display)
                                            break
                                        elif display.button_fullscreenise_rect is not None and display.button_fullscreenise_rect.collidepoint(event.pos):
                                            display.do_fullscreenise()
                                            display.draw_map(d)
                                            display.blit_all_to_screen(d) 
                                            conf.set("display","fullscreen","on")
                                            file = open("config/config.txt","w")
                                            conf.write(file)
                                            file.close()
                                        elif display.button_windowise_rect is not None and display.button_windowise_rect.collidepoint(event.pos):
                                            display.do_windowise()
                                            display.draw_map(d)
                                            display.blit_all_to_screen(d) 
                                            conf.set("display","fullscreen","off")
                                            file = open("config/config.txt","w")
                                            conf.write(file)
                                            file.close()
                                        elif display.button_iconify_rect is not None and display.button_iconify_rect.collidepoint(event.pos):
                                            display.do_iconify()
                                        elif display.button_quit_rect is not None and display.button_quit_rect.collidepoint(event.pos):
                                            quit_game(d)
                                            break
                                        elif display.button_sound_rect is not None and display.button_sound_rect.collidepoint(event.pos):
                                            sound.use_sound = 1-sound.use_sound
                                            display.refill_character_pane(d)
                                            display.blit_all_to_screen(d)
                                            if sound.use_sound:
                                                conf.set("sound","sound","on")
                                            else:
                                                conf.set("sound","sound","off")
                                            file = open("config/config.txt","w")
                                            conf.write(file)
                                            file.close()
                                        else:
                                            d.glyphs = [Glyph(d,mouse_button_down_time,mouse_button_up_time,mouse_button_pos_series)] + d.glyphs
                                            display.show_glyphs(d)
                                            display.blit_map_to_screen() 
                                # so what are they going to do?
                                if len(d.current_actor.action_queue) > 0:
                                    do_delayed_events=1
                                    for glyph in d.glyphs:
                                        if mouse_pos_to_map_pos(d,display,glyph.last_pos) is None:
                                            do_delayed_events = 0
                                            break
                                    if do_delayed_events:
                                        # there are delayed events - put them back on the queue
                                        a = d.current_actor.action_queue
                                        d.current_actor.action_queue = []
                                    else:
                                        # cancel the delayed events
                                        d.current_actor.action_queue = []                                
                                elif len(d.glyphs)>0 and mouse_button_down == 0 and pygame.time.get_ticks()>mouse_button_up_time+500:
                                    last_glyph = d.glyphs[0]
                                    if len(d.glyphs)>1:
                                        second_last_glyph = d.glyphs[1]
                                    else:
                                        second_last_glyph = None
                                    # try for an action
                                    used_power = 0
                                    for power in d.get_character().info().powers:
                                        valid, array_of_actions, n_glyphs_to_remove = activate_power(d,display,d.glyphs,power)
                                        if valid:
                                            a = a + array_of_actions
                                            used_power = 1
                                            d.get_character().powers_used[power] = d.turn
                                            d.glyphs = []
                                            display.draw_map(d)
                                            display.show_glyphs(d)
                                            display.blit_map_to_screen() 
                                            break
                                    if used_power==0: 
                                        if display.garb_rect.collidepoint(last_glyph.first_pos) and d.get_character().garb is not None:  
                                            if d.try_drop(display,mouse_pos_to_map_pos(d,display,last_glyph.last_pos),d.get_character().pos,SL_GARB):
                                                pass
                                            else:
                                                do_chardump = display.info_popup(object=d.get_character().garb,entity=d.get_character(),chardump_option=1) # pass it entity to get stats
                                                if do_chardump:
                                                    files.do_chardump(d,display)
                                            display.draw_map(d)
                                            d.glyphs = d.glyphs[1:]
                                            display.show_glyphs(d)
                                            display.blit_map_to_screen() 
                                            break
                                        if display.melee_weapon_rect.collidepoint(last_glyph.first_pos) and d.get_character().melee_weapon is not None:  
                                            if d.try_drop(display,mouse_pos_to_map_pos(d,display,last_glyph.last_pos),d.get_character().pos,SL_MELEE):
                                                pass
                                            else:
                                                display.info_popup(object=d.get_character().melee_weapon)
                                            d.glyphs = d.glyphs[1:]
                                            display.draw_map(d)
                                            display.show_glyphs(d)
                                            display.blit_map_to_screen() 
                                            break
                                        if display.missile_weapon_rect.collidepoint(last_glyph.first_pos) and d.get_character().missile_weapon is not None:  
                                            if d.try_drop(display,mouse_pos_to_map_pos(d,display,last_glyph.last_pos),d.get_character().pos,SL_MISSILE):
                                                pass
                                            else:
                                                display.info_popup(object=d.get_character().missile_weapon)
                                            d.glyphs = d.glyphs[1:]
                                            display.draw_map(d)
                                            display.show_glyphs(d)
                                            display.blit_map_to_screen() 
                                            break
                                        item_clicked = 0
                                        for i in range(0,6):                                                
                                            if display.item_rects[i].collidepoint(last_glyph.first_pos) and d.get_character().items[i] is not None:  
                                                if d.try_drop(display,mouse_pos_to_map_pos(d,display,last_glyph.last_pos),d.get_character().pos,SL_MISC_ITEM,number=i):
                                                    pass
                                                elif d.get_character().items[i].info().consumable:
                                                    d.get_character().consume(d,d.get_character().items[i])
                                                    d.get_character().items[i] = None
                                                    d.get_character().stats.recalc(d)
                                                else:
                                                    display.info_popup(object=d.get_character().items[i])
                                                d.glyphs = d.glyphs[1:]
                                                display.draw_map(d)
                                                display.show_glyphs(d)
                                                display.blit_map_to_screen() 
                                                item_clicked = 1
                                                break
                                        if item_clicked:
                                            break
                                        key_clicked = 0
                                        for i in range(0,5):                                                
                                            if display.key_rects[i].collidepoint(last_glyph.first_pos) and d.get_character().keys[i] is not None:  
                                                if d.try_drop(display,mouse_pos_to_map_pos(d,display,last_glyph.last_pos),d.get_character().pos,SL_KEY,number=i):
                                                    pass
                                                else:
                                                    display.info_popup(object=d.get_character().keys[i])
                                                d.glyphs = d.glyphs[1:]
                                                display.draw_map(d)
                                                display.show_glyphs(d)
                                                display.blit_map_to_screen() 
                                                key_clicked = 1
                                                break
                                        if key_clicked:
                                            break
                                        down_right = display.get_mouse_down_right(d.glyphs[0].last_pos)
                                        if down_right is not None:
                                            d.glyphs = []
                                            display.draw_map(d)
                                            display.show_glyphs(d)
                                            display.blit_map_to_screen() 
                                            mouse_pos = d.current_actor.pos.copy()
                                            mouse_pos.down,mouse_pos.right = down_right[0],down_right[1]
                                            dir = d.current_actor.pos.direction_towards(mouse_pos) 
                                            if dir==DIR_HERE and not d.l.tile_at(d.current_actor.pos).info().is_portal:
                                                # do nothing
                                                a.append(Action(d.current_actor,A_NOTHING,None,None))
                                            elif dir==DIR_HERE:
                                                # must be a portal, step through
                                                a.append(Action(d.current_actor,A_USE_PORTAL,d.l.tile_at(d.current_actor.pos).to_pos,None))
                                            elif adjacent(d,d.current_actor.pos,mouse_pos):
                                                done = 0
                                                if not d.l.tile_at(mouse_pos).info().walkable(d.current_actor):
                                                    if d.get_occupant(mouse_pos) is None:
                                                        sound.play("cancel1",0.6)
                                                        display.send_message("Your path is blocked!")
                                                        done = 1
                                                if not done:
                                                    # single step or hit adjacent enemy
                                                    old_pos = d.get_active_pos().copy()
                                                    new_pos = get_pos_in_dir(d,old_pos,dir)
                                                    next_action = make_basic_move_action(d,d.current_actor,new_pos)                                                   
                                                    a.append(next_action)
                                                    # if at a portal or exit, add an action to go through it
                                                    new_tile = d.l.tile_at(new_pos)
                                                    if new_tile.info().is_portal and next_action.type==A_WALK:
                                                        move_code, target = d.current_actor.can_move(d,new_tile.to_pos)
                                                        if move_code is not M_FAIL:
                                                            a.append(Action(d.current_actor,A_USE_PORTAL,new_tile.to_pos.copy(),target))
                                                        elif new_tile.info().is_exit:
                                                            a.append(Action(d.current_actor,A_USE_EXIT,None,None))
                                            elif not d.l.tile_at(mouse_pos).info().walkable(d.current_actor) and not (d.l.tile_at(mouse_pos).info().can_be_unlocked):
                                                sound.play("cancel1",0.6)
                                                display.send_message("Your path is blocked!")
                                            else:
                                                # multistep (not attack)
                                                old_pos = d.current_actor.pos.copy()
                                                if (straight_line_distance([old_pos.down,old_pos.right],[mouse_pos.down,mouse_pos.right]) < 4):
                                                    entities_to_dodge = d.get_entities_in_room(old_pos) # dodge monsters only if it's a short walk (otherwise get funny path and potentially hit on the way)
                                                    exclude = []
                                                    for entity in entities_to_dodge:
                                                        exclude.append(entity.pos)
                                                else:
                                                    exclude = []
                                                if not d.l.tile_at(mouse_pos).info().can_be_unlocked:
                                                    to_pos = mouse_pos
                                                else:
                                                    to_pos = mouse_pos
                                                    this_room = d.l.room_at(mouse_pos)
                                                    for dir in [DIR_UP,DIR_DOWN,DIR_LEFT,DIR_RIGHT]:
                                                        try_pos = get_pos_in_dir(d,mouse_pos,dir)
                                                        if try_pos.right>=0 and try_pos.right<this_room.width and try_pos.down>=0 and try_pos.down<this_room.height:
                                                            if d.l.tile_at(try_pos).info().walkable(d.current_actor):
                                                                path,junk = astar_pathfind_withinroom(d,old_pos,try_pos,exclude,d.current_actor.info().can_swim,max_range=100) # or max_range=40 for dumb pathfinding, and below, and uncomment some stuff below
                                                                if path is not None:
                                                                    to_pos = try_pos
                                                                    break
                                                path,junk = astar_pathfind_withinroom(d,old_pos,to_pos,exclude,d.current_actor.info().can_swim,max_range=100) # or max_range=40 for dumb pathfinding, and above, and uncomment some stuff below
                                                if path is not None:
                                                    first_step = 1
                                                    for step in path:
                                                        step_pos = old_pos.copy()
                                                        step_pos.down = step[0]
                                                        step_pos.right = step[1]
                                                        next_action = make_basic_move_action(d,d.current_actor,step_pos) 
                                                        if next_action.type==A_WALK:
                                                            a.append(next_action)
                                                            first_step = 0
                                                            # if at a portal or exit, add an action to go through it
                                                            new_tile = d.l.tile_at(step_pos)
                                                            if new_tile.info().is_portal:
                                                                move_code, target = d.current_actor.can_move(d,new_tile.to_pos)
                                                                if move_code is not M_FAIL:
                                                                    a.append(Action(d.current_actor,A_USE_PORTAL,new_tile.to_pos.copy(),target))
                                                                elif new_tile.info().is_exit:
                                                                    a.append(Action(d.current_actor,A_USE_EXIT,None,None))
                                                                break      
                                                        else:
                                                            if first_step:
                                                                sound.play("cancel1",0.6)
                                                                display.send_message("Your path is blocked!")
                                                            break
                                                else:
                                                    # unlimited_length_path,junk = astar_pathfind_withinroom(d,old_pos,mouse_pos,exclude,d.current_actor.info().can_swim)
                                                    #sound.play("cancel1",0.6)
                                                    #if unlimited_length_path is None:
                                                    display.send_message("Your path is blocked!")
                                                    #else:
                                                    #    display.send_message("Your hero can't figure out how to get there!")
                                                if d.l.tile_at(mouse_pos).info().can_be_unlocked:
                                                    pass # TO DO. Can we somehow add a 'try to unlock' action here? Otherwise they will walk up to the door but not actually open it...
                                elif len(events)==0 & len(d.current_actor.action_queue)==0:
                                    wait(20)
                            elif d.current_actor.is_character and not confused and d.current_actor.out_of_control():
                                if len(d.current_actor.action_queue)>0:
                                    a = d.current_actor.action_queue
                                    d.current_actor.action_queue = []
                                elif len(a)==0:
                                    a.append(d.current_actor.brain_when_out_of_control.get_action(d))
                            elif not confused:
                                if len(d.current_actor.action_queue)>0:
                                    a = d.current_actor.action_queue
                                    d.current_actor.action_queue = []
                                elif len(a)==0:
                                    a.append(d.current_actor.brain.get_action(d))
                            while len(a)>0:
                                action = a[0]
                                if not d.current_actor.no_longer_here():
                                    if d.current_actor.energy < 0:
                                        d.current_actor.action_queue = a
                                        a = []
                                        break
                                    action.resolve(d) 
                                    d.current_actor.after_action_update(d)
                                a = a[1:]
                    else:
                        d.finished_group_moves = 1

# do some psyco bindings 
psyco.bind(play_game)

# play the game!
try:
    ack = open("ACKNOWLEDGEMENTS.txt","r")
except:
    display.crash("Could not load acknowledgements file")
text = []
for line in ack:
    text.append(line[:len(line)-1])
text.append("")
text.append("Click to continue...")
GuilessSplashScreen(None,text,allow_quit=1)

if files.n_of_save_files("save")>0:
    option = GuilessDialog("Begin",None,"Do you want to start a new game, open a saved game, or play the tutorial?",70,["New","Open","Tutorial"],['n','o','t'],is_fullscreen=display.is_fullscreen,allow_quit=1)
else:
    option = GuilessDialog("Begin",None,"Do you want to play the tutorial, or are you ready to start a new game?",70,["New","Tutorial"],['n','t'],is_fullscreen=display.is_fullscreen,allow_quit=1)    
if option=="Open":
    savefilename = get_savefile_to_open(0)
    if savefilename is not None:
        outcome = load_game(savefilename)
    else:
        raise SystemExit
elif option=="New":
    outcome = new_game(1)
elif option=="Tutorial":
    outcome = tutorial(0)
while outcome[0]==1 or outcome[0]==2: # new game or load game, respectively
    if outcome[0]==1:
        outcome = new_game(1)
    elif outcome[0]==2:
        outcome = load_game(outcome[1])

#import hotshot
#profiler = hotshot.Profile("C:\\roguelike\\orbquest\\profile.prof")
#profiler.run('new_game(1)')
#profiler.close() 
# then:
# import hotshot, hotshot.stats
# stats = hotshot.stats.load("C:\\roguelike\\orbquest\\profile.prof")
# stats.strip_dirs()
# stats.sort_stats('time')
# stats.sort_stats('cumulative')
# stats.print_stats(20)
# stats.print_callers()
# stats.print_callees()

